﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Demos.DataStructures;

namespace Demos.Hashmap
{
    class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private string _nuevaPalabraNombre;
        public string NuevaPalabraNombre
        {
            get { return _nuevaPalabraNombre; } 
            set
            {
                _nuevaPalabraNombre = value;
                NotifyPropertyChanged("NuevaPalabraNombre");
            }
        }

        private string _nuevaPalabraOrigen;
        public string NuevaPalabraOrigen
        {
            get { return _nuevaPalabraOrigen; }
            set
            {
                _nuevaPalabraOrigen = value;
                NotifyPropertyChanged("NuevaPalabraOrigen");
            }
        }

        private int _pestañaSeleccionada;
        public int PestañaSeleccionada
        {
            get { return _pestañaSeleccionada; }
            set
            {
                _pestañaSeleccionada = value;
                NotifyPropertyChanged("PestañaSeleccionada");
            }
        }

        public ObservableCollection<Definicion> NuevaPalabraDefiniciones { get; set; }

        private string _palabraBuscada;
        public string PalabraBuscada
        {
            get { return _palabraBuscada; }
            set
            {
                _palabraBuscada = value;
                NotifyPropertyChanged("PalabraBuscada");
            }
        }

        public ObservableCollection<Palabra> Palabras { get; set; }
        public ObservableCollection<ElementoVisual> HashMapViewElements { get; set; }

        private Dictionary<int, List<Palabra>> _hashList;

        private bool _verInsercion;
        public Visibility VerInsercion
        {
            get
            {
                if (_verInsercion)
                {
                    return Visibility.Visible;
                }
                return Visibility.Hidden;
            }
        }

        public ICommand CerrarPestañasCommand { get; private set; }
        public ICommand BuscarPalabraCommand { get; private set; }
        public ICommand ComenzarInsercionCommand { get; private set; }
        public ICommand AñadirMasDefinicionesCommand { get; private set; }
        public ICommand InsertarCommand { get; private set; }
        public ICommand CancelarCommand { get; private set; }
        public ICommand VerHashMapCommand { get; private set; }
        
        private ObservableHashMap _model;


        public ViewModel(ObservableHashMap model)
        {
            _model = model;
            NuevaPalabraDefiniciones = new ObservableCollection<Definicion> { new Definicion("") };
            Palabras = new ObservableCollection<Palabra>();
            PalabraBuscada = "Buscar palabra...";
            NuevaPalabraNombre = "";
            CerrarPestañasCommand = new RelayCommand(() => Palabras.Clear(), () => Palabras.Count > 0);
            BuscarPalabraCommand = new RelayCommand(BuscarPalabra, () => PalabraBuscada.Count() > 0);
            AñadirMasDefinicionesCommand = new RelayCommand(AñadirMasDefiniciones, () => true);
            InsertarCommand = new RelayCommand(InsertarPalabra, () => NuevaPalabraNombre.Count() > 0);
            CancelarCommand = new RelayCommand(CancelarInsercion, () => true);
            ComenzarInsercionCommand = new RelayCommand(ComenzarInsercion, () => true);
            VerHashMapCommand = new RelayCommand(VerHashMap, () => true);
            _hashList = new Dictionary<int, List<Palabra>>();
            _model.Cambio += HashmapCambiado;

            //palabras de prueba
            _model["a"] = new Palabra("a", new List<string>() { "def1" }, "origen");
            _model["aa"] = new Palabra("aa", new List<string>() { "def1", "def2" });
            _model["Seattle"] = new Palabra("Seattle", new List<string>() { "def1", "def2", "def3" }, "origen");
            _model["Acetato"] = new Palabra("Acetato", new List<string>() { "def1" }, "origen");
        }


        private void HashmapCambiado(int hash, KeyValuePair<string, Palabra> clavevalor)
        {
            if (_hashList.ContainsKey(hash))
            {
                _hashList[hash].Add(clavevalor.Value);
            }
            else
            {
                _hashList.Add(hash, new List<Palabra> {clavevalor.Value});
            }
        }

        private void VerHashMap()
        {
            DibujarHashMap();
            var vistaHashMap = new hashmapView {DataContext = this};
            vistaHashMap.Show();
        }

        private void DibujarHashMap()
        {
            HashMapViewElements = new ObservableCollection<ElementoVisual>();
            const int anchuraCaja = 50;
            const int alturaCaja = 25;
            const int espacio = 5;
            DibujarPalabras(alturaCaja, anchuraCaja, espacio);
            DibujarHashes(alturaCaja, espacio);
            DibujarDefiniciones(alturaCaja, espacio);
        }

        private void DibujarPalabras(int alturaCaja, int anchuraCaja, int espacio)
        {
            double alturaSiguiente = 0;
            const double anchuraSiguiente = 0;
            foreach (var palabra in _model.Keys())
            {
                HashMapViewElements.Add(
                    new Caja(anchuraSiguiente, alturaSiguiente, alturaCaja, anchuraCaja, palabra));
                alturaSiguiente += alturaCaja + espacio;
            }
        }

        private void DibujarHashes(int alturaCaja, int espacio)
        {
            const double anchuraSiguiente = 100;
            double alturaSiguiente = 0;
            const int anchuraCaja = 30;
            foreach (var hash in _hashList.Keys)
            {
                HashMapViewElements.Add(
                    new Caja(anchuraSiguiente, alturaSiguiente, alturaCaja, anchuraCaja, hash.ToString()));
                alturaSiguiente += alturaCaja;
                foreach (var palabra in _hashList[hash])
                {
                    var palabra2 = palabra;
                    var indice = _model.Keys().TakeWhile(palabra1 => palabra1 != palabra2.Nombre).Count();
                    var yFlechaOrigen = indice * alturaCaja + espacio + (alturaCaja / 2);
                    const int xFlechaOrigen = 50;
                    var yFlechaDestino = alturaSiguiente - (alturaCaja / 2);
                    const double xFlechaDestino = anchuraSiguiente;
                    HashMapViewElements.Add(
                        new Flecha(xFlechaOrigen, yFlechaOrigen, xFlechaDestino, yFlechaDestino));
                }
            }
        }

        private void DibujarDefiniciones(int alturaCaja, int espacio)
        {
            double anchuraSiguiente = 150;
            double alturaSiguiente = 0;
            const int anchuraCaja = 80;
            var fila = 0;
            foreach (var hash in _hashList.Keys)
            {
                HashMapViewElements.Add(
                        new Flecha(130, fila * alturaCaja + alturaCaja / 2, anchuraSiguiente, fila * alturaCaja + alturaCaja / 2));

                int indiceDefiniciones = 0;

                foreach (var palabra in _hashList[hash])
                {
                    HashMapViewElements.Add(
                    new Caja(anchuraSiguiente, alturaSiguiente, alturaCaja, anchuraCaja, palabra.Nombre));
                    anchuraSiguiente += anchuraCaja;

                    foreach (var definicion in palabra.definiciones)
                    {
                        HashMapViewElements.Add(
                        new Caja(anchuraSiguiente, alturaSiguiente, alturaCaja, anchuraCaja, definicion));
                        anchuraSiguiente += anchuraCaja;
                    }

                    if (indiceDefiniciones < _hashList[hash].Count - 1)
                    {
                        HashMapViewElements.Add(
                        new Flecha(anchuraSiguiente, fila * alturaCaja + alturaCaja / 2, anchuraSiguiente + espacio, fila * alturaCaja + alturaCaja / 2));
                    }

                    anchuraSiguiente += espacio;
                    indiceDefiniciones++;
                }
                alturaSiguiente += alturaCaja;
                anchuraSiguiente = 150;
                fila++;
            }
        }

        private void AñadirMasDefiniciones()
        {
            NuevaPalabraDefiniciones.Add(new Definicion(""));
            NotifyPropertyChanged("NuevaPalabraDefiniciones");
        }

        private void ComenzarInsercion()
        {
            NuevaPalabraNombre = "";
            NuevaPalabraOrigen = "";
            NuevaPalabraDefiniciones = new ObservableCollection<Definicion> {new Definicion("")};
            NotifyPropertyChanged("NuevaPalabraDefiniciones");
            _verInsercion = true;
            NotifyPropertyChanged("VerInsercion");
        }

        private void CancelarInsercion()
        {
            _verInsercion = false;
            NotifyPropertyChanged("VerInsercion");
        }

        private void InsertarPalabra()
        {
            _model[NuevaPalabraNombre] = new Palabra(NuevaPalabraNombre,  
                (from def in NuevaPalabraDefiniciones where !string.IsNullOrEmpty(def.Texto) select def.Texto).ToList(), 
                NuevaPalabraOrigen);
            _verInsercion = false;
            NotifyPropertyChanged("VerInsercion");
            DibujarHashMap();
            NotifyPropertyChanged("HashMapViewElements");
        }

        private void BuscarPalabra()
        {
            try
            {
                var p = _model[PalabraBuscada];
                Palabras.Add(p);
            }
            catch (KeyNotFoundException)
            {
                Palabras.Add(new Palabra(PalabraBuscada, new List<string>(), "No encontrada"));
            }
            finally
            {
                PestañaSeleccionada = Palabras.Count - 1;
            }
        }
    }
}
